package adc.app.ui.wicket;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.authorization.IAuthorizationStrategy;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
import org.apache.wicket.feedback.ContainerFeedbackMessageFilter;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.CheckGroup;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;

import adc.app.spec.IBo;
import adc.app.spec.IUser;
import adc.app.ui.wicket.security.AuthenticationRequired;
import adc.app.ui.wicket.security.ComponentAction;
import adc.app.ui.wicket.security.Right;
import adc.app.ui.wicket.security.RightRequired;
import adc.app.ui.wicket.table.AbstractLinkColumn;
import adc.app.ui.wicket.table.DataTable;
import adc.app.ui.wicket.table.DeleteCheckPropertyColumn;
import adc.app.ui.wicket.table.StyledColumn;

@AuthenticationRequired
@RightRequired(value=Right.VIEW)
public abstract class BoListPanel<T extends IBo> extends Panel {
	private static final long serialVersionUID = 1L;
	
	private static final String COMP_ID_FEEDBACK = "feedback";
	private static final String COMP_ID_FORM = "form";
	private static final String COMP_ID_SELECTION = "selection";
	private static final String COMP_ID_TABLE = "table";
	
	public interface IAddCallback extends Serializable {
		void add(AjaxRequestTarget target);
	}
	
	public interface IEditCallback<T extends IBo> extends Serializable {
		void edit(AjaxRequestTarget target, IModel<T> domainObject);
	}
	
	public interface IDeleteCallback extends Serializable {
		void delete(AjaxRequestTarget target, Set<Long> domainObjectIds);
	}
	
	private final ISortableDataProvider<T> dataProvider;
	private IAddCallback addCallback;
	private IEditCallback<T> editCallback;
	private IDeleteCallback deleteCallback;
	
	public BoListPanel(final String id, final ISortableDataProvider<T> dataProvider) {
		super(id);
		this.dataProvider = dataProvider;
		
		final Form<?> form = new Form<Object>(COMP_ID_FORM);
		this.add(form);
		
		final Component mockupTable = new WebComponent(COMP_ID_TABLE);
		
		form.add(new CheckGroup<Long>(COMP_ID_SELECTION, new HashSet<Long>()).add(mockupTable));
		add(new AlertFeedbackPanel(COMP_ID_FEEDBACK, new ContainerFeedbackMessageFilter(form))
			.setOutputMarkupId(true));
	}
	
	
	
	@Override
	protected void onBeforeRender() {
		super.onBeforeRender();
		Component table = this.get(COMP_ID_FORM + PATH_SEPARATOR + COMP_ID_SELECTION + PATH_SEPARATOR + COMP_ID_TABLE);
		if (!(table instanceof ObjectsTable<?>)) {
			IAuthorizationStrategy authorStrategy = Application.get().getSecuritySettings().getAuthorizationStrategy();
			if (authorStrategy != null && authorStrategy.isInstantiationAuthorized(ObjectsTable.class)) {
				String[] modifLinkColumnProperty = modifLinkColumnProperty();
				ModifyObjectLinkColumn<T> modifCol = null;
				if (modifLinkColumnProperty != null && modifLinkColumnProperty.length > 0) {
					modifCol = new ModifyObjectLinkColumn<T>(
							modifLinkColumnTitle(),
							modifLinkColumnProperty[0],
							modifLinkColumnProperty.length > 1 ? modifLinkColumnProperty[1] : null);
				}
				ObjectsTable<T> objsTable = new ObjectsTable<T>(COMP_ID_TABLE,
						new StyledColumn<T>(createCheckColumn()).setCssClass("adc-check-column"), modifCol,
						additionalColumns(), dataProvider);
				objsTable.addLink(new AddObjectButton<T>(objsTable.getLinkId(), objsTable.getLinkLabelId()));
				objsTable.addLink(new DeleteObjectsButton<T>(objsTable.getLinkId(), objsTable.getLinkLabelId()));
				table.replaceWith(objsTable.setOutputMarkupId(true));
			}
		}
	}

	public final IAddCallback getAddCallback() {
		return addCallback;
	}

	public final BoListPanel<T> setAddCallback(IAddCallback addCallback) {
		this.addCallback = addCallback;
		return this;
	}

	public final IEditCallback<T> getEditCallback() {
		return editCallback;
	}

	public final BoListPanel<T> setEditCallback(IEditCallback<T> editCallback) {
		this.editCallback = editCallback;
		return this;
	}

	public final IDeleteCallback getDeleteCallback() {
		return deleteCallback;
	}

	public final BoListPanel<T> setDeleteCallback(IDeleteCallback deleteCallback) {
		this.deleteCallback = deleteCallback;
		return this;
	}

	public final ISortableDataProvider<T> getDataProvider() {
		return dataProvider;
	}

	protected List<IColumn<T>> additionalColumns() {
		return null;
	}

	protected abstract IModel<String> modifLinkColumnTitle();
	
	protected abstract String[] modifLinkColumnProperty();
	
	protected IColumn<T> createCheckColumn() {
		return new DeleteCheckPropertyColumn<T>("id");
	}
	
	public void updateTable(AjaxRequestTarget target) {
		Component table = this.get(COMP_ID_FORM + PATH_SEPARATOR + COMP_ID_SELECTION + PATH_SEPARATOR + COMP_ID_TABLE);
		if (table instanceof ObjectsTable<?> && target != null) {
			target.add(table);
		}
	}
	
	@AuthenticationRequired(forAction=ComponentAction.CREATE)
	@RightRequired(value=Right.VIEW, forAction=ComponentAction.CREATE)
	private static final class ObjectsTable<T extends IBo> extends DataTable<T> {

		private static final long serialVersionUID = 1L;

		private static final <T extends IBo> IColumn<T>[] columnsArray(
				IColumn<T> checkColumn,
				ModifyObjectLinkColumn<T> modifColumn,
				List<IColumn<T>> additionalColumns) {
			int standardCols = 0;
			if (checkColumn != null) {
				standardCols ++;
			}
			if (modifColumn != null) {
				standardCols ++;
			}
			int arraySize = standardCols;
			if (additionalColumns != null && !additionalColumns.isEmpty()) {
				arraySize += additionalColumns.size();
			}
			@SuppressWarnings("unchecked")
			IColumn<T>[] columns = new IColumn[arraySize];
			int i = 0;
			if (checkColumn != null) {
				columns[i++] = checkColumn;
			}
			if (modifColumn != null) {
				columns[i++] = modifColumn;
			}
			for (Iterator<IColumn<T>> iter = additionalColumns.iterator(); iter.hasNext() && i < columns.length; i++) {
				columns[i] = iter.next();
			}
			return columns;
		}
		
		public ObjectsTable(String id, IColumn<T> checkColumn,
				ModifyObjectLinkColumn<T> modifColumn, List<IColumn<T>> additionalColumns,
				ISortableDataProvider<T> dataProvider) {
			super(id, columnsArray(checkColumn, modifColumn, additionalColumns), dataProvider, 10);
		}
		
		
	}
	
	private static final class ModifyObjectLinkColumn<T extends IBo> extends AbstractLinkColumn<T> {

		private static final long serialVersionUID = 1L;

		private final String prop;
		
		public ModifyObjectLinkColumn(IModel<String> titleModel, String property, String sortProperty) {
			super(titleModel, sortProperty);
			prop = property;
		}

		@Override
		protected LabeledLinkPanel createLink(String idLink, IModel<T> model) {
			final LabeledLinkPanel llp = new LabeledLinkPanel(idLink, new PropertyModel<IUser>(model, prop));
			AjaxFallbackLink<T> link = new AjaxFallbackLink<T>(llp.getLinkId()) {

				private static final long serialVersionUID = 1L;

				@Override
				public void onClick(AjaxRequestTarget target) {
					@SuppressWarnings("unchecked")
					BoListPanel<T> listPanel = (BoListPanel<T>) 
						this.findParent(BoListPanel.class);
					if (listPanel.editCallback != null) {
						listPanel.editCallback.edit(target, this.getModel());
					}
				}
				
			};
			link.setModel(model);
			llp.setLink(link);
			return llp;
		}
	}
	
	@RightRequired(Right.CREATE)
	private static final class AddObjectButton<T extends IBo> extends AjaxLink<T> {

		private static final long serialVersionUID = 1L;

		public AddObjectButton(String id, String labelId) {
			super(id);
			add(new Label(labelId, new ResourceModel(IMsgs.ADD)));
		}

		@Override public void onClick(AjaxRequestTarget target) {
			@SuppressWarnings("unchecked")
			BoListPanel<T> listPanel = (BoListPanel<T>)
				this.findParent(BoListPanel.class);
			if (listPanel.addCallback != null) {
				listPanel.addCallback.add(target);
			}
		}
	}
	
	@RightRequired(Right.DELETE)
    private static final class DeleteObjectsButton<T extends IBo> extends AjaxSubmitLink {

		private static final long serialVersionUID = 1L;

		public DeleteObjectsButton(String id, String labelId) {
			super(id);
			add(new Label(labelId, new ResourceModel(IMsgs.DELETE)));
		}
		
		@Override
		protected void onError(AjaxRequestTarget target, Form<?> form) {
			target.add(form.getParent().get(COMP_ID_FEEDBACK));
		}



		@Override
		protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
			@SuppressWarnings("unchecked")
			BoListPanel<T> listPanel = (BoListPanel<T>)
				this.findParent(BoListPanel.class);
			Component selection = listPanel.get(COMP_ID_FORM + PATH_SEPARATOR + COMP_ID_SELECTION);
			@SuppressWarnings("unchecked")
			Set<Long> objectIds = (Set<Long>) selection.getDefaultModelObject();
			if (listPanel.deleteCallback != null) {
				listPanel.deleteCallback.delete(target, objectIds);
			}
		}

		
	}
}
